package com.sc.nft.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.meta.component.base.api.CommonResult;
import com.meta.component.base.api.ResultCode;
import com.meta.push.plat.client.api.MetaPushPlatClient;
import com.meta.push.plat.client.enums.PushBusinessTypeEnum;
import com.meta.push.plat.client.enums.PushTypeEnum;
import com.meta.push.plat.client.req.PushMessageBodyRequest;
import com.meta.push.plat.client.req.PushMessageRequest;
import com.sc.nft.dao.*;
import com.sc.nft.dto.PushMessageExtendDTO;
import com.sc.nft.entity.*;
import com.sc.nft.entity.vo.PushManageVo;
import com.sc.nft.enums.*;
import com.sc.nft.exception.GlobalRunTimeException;
import com.sc.nft.service.PushService;
import com.sc.nft.sup.ErrorCode;
import com.sc.nft.sup.PageResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 刘国航
 * @desc
 * @since 2024/6/14 16:19
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class PushServiceImpl implements PushService {

    private final PushDao pushDao;
    private final NoticeInfoDao noticeInfoDao;
    private final PushRelationTagDao pushRelationTagDao;
    private final RedissonClient redissonClient;
    private final PushTagDao pushTagDao;
    private final UserCollectionDao userCollectionDao;
    private final PushTagRelationUserDao pushTagRelationUserDao;
    private final UserRelationPushCidDao userRelationPushCidDao;
    private final MetaPushPlatClient metaPushPlatClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addPush(String pushTitle, String pushContent, String pushIcon, PushManageTypeEnum pushType, Boolean enableSchedulePush, Date schedulePushTime, List<Long> pushTagIdList) {
        checkAddPush(pushTitle, pushType, enableSchedulePush, schedulePushTime, pushTagIdList);
        Push push = new Push();
        push.setPushTitle(pushTitle);
        push.setPushContent(pushContent);
        push.setPushIcon(pushIcon);
        push.setPushType(pushType);
        push.setEnableSchedulePush(enableSchedulePush);
        push.setSchedulePushTime(schedulePushTime);
        push.setIsPushed(false);
        push.setStatus(false);
        push.insert();
        if (Objects.equals(pushType.getCode(), PushManageTypeEnum.TAG_PUSH.getCode())) {
            pushTagIdList.forEach(pushTagId -> {
                PushRelationTag pushRelationTag = new PushRelationTag();
                pushRelationTag.setPushId(push.getId());
                pushRelationTag.setPushTagId(pushTagId);
                pushRelationTag.insert();
            });
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editPush(Long id, String pushTitle, String pushContent, String pushIcon, PushManageTypeEnum pushType, Boolean enableSchedulePush, Date schedulePushTime, List<Long> pushTagIdList) {
        Push push = checkEditPush(id, pushTitle, pushType, enableSchedulePush, schedulePushTime, pushTagIdList);
        push.setPushTitle(pushTitle);
        push.setPushContent(pushContent);
        push.setPushIcon(pushIcon);
        push.setPushType(pushType);
        push.setEnableSchedulePush(enableSchedulePush);
        push.setSchedulePushTime(schedulePushTime);
        pushDao.updateById(push);

        List<PushRelationTag> pushRelationTagList = pushRelationTagDao.getListByPushId(push.getId());
        if (CollectionUtil.isNotEmpty(pushRelationTagList)) {
            pushRelationTagList.forEach(pushRelationTag -> pushRelationTagDao.removeById(pushRelationTag.getId()));
        }

        if (Objects.equals(pushType.getCode(), PushManageTypeEnum.TAG_PUSH.getCode())) {
            pushTagIdList.forEach(pushTagId -> {
                PushRelationTag pushRelationTag = new PushRelationTag();
                pushRelationTag.setPushId(push.getId());
                pushRelationTag.setPushTagId(pushTagId);
                pushRelationTag.insert();
            });
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePush(Long id) {
        Push push = pushDao.getById(id);
        if (Objects.isNull(push)) {
            throw new GlobalRunTimeException("推送不存在");
        }
        if (push.getStatus()) {
            throw new GlobalRunTimeException("上架中，不能删除");
        }
        pushDao.removeById(push.getId());
        if (Objects.equals(push.getPushType().getCode(), PushManageTypeEnum.TAG_PUSH.getCode())) {
            List<PushRelationTag> pushRelationTagList = pushRelationTagDao.getListByPushId(push.getId());
            if (CollectionUtil.isNotEmpty(pushRelationTagList)) {
                pushRelationTagList.forEach(pushRelationTag -> pushRelationTagDao.removeById(pushRelationTag.getId()));
            }
        }
    }

    @Override
    public PageResult<PushManageVo> listPage(Integer pageNo, Integer pageSize, String pushTitle) {
        Page<Push> pushPage = pushDao.pageByTitle(new Page<>(pageNo, pageSize), pushTitle);
        List<PushManageVo> pushManageVoList = new ArrayList<>();
        List<Push> records = pushPage.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            List<Long> pushIdList = records.stream().map(Push::getId).collect(Collectors.toList());
            List<PushRelationTag> pushRelationTagList = pushRelationTagDao.getListByPushIdList(pushIdList);
            Map<Long, List<PushRelationTag>> pushRelationTagMap = CollectionUtil.isNotEmpty(pushRelationTagList) ? pushRelationTagList.stream().collect(Collectors.groupingBy(PushRelationTag::getPushId)) : new HashMap<>();
            records.forEach(record -> {
                PushManageVo pushManageVo = new PushManageVo();
                pushManageVo.setId(record.getId());
                pushManageVo.setPushTitle(record.getPushTitle());
                pushManageVo.setPushContent(record.getPushContent());
                pushManageVo.setPushIcon(record.getPushIcon());
                pushManageVo.setPushType(record.getPushType());
                pushManageVo.setEnableSchedulePush(record.getEnableSchedulePush());
                pushManageVo.setSchedulePushTime(record.getSchedulePushTime());
                pushManageVo.setStatus(record.getStatus());
                if (CollectionUtil.isNotEmpty(pushRelationTagMap.get(record.getId()))) {
                    List<Long> pushTagIdList = pushRelationTagMap.get(record.getId()).stream().map(PushRelationTag::getPushTagId).distinct().collect(Collectors.toList());
                    pushManageVo.setPushTagIdList(pushTagIdList);
                }
                pushManageVoList.add(pushManageVo);
            });
        }
        PageResult<PushManageVo> manageVoPageResult = new PageResult<>();
        manageVoPageResult.setCurrentPage(pushPage.getCurrent());
        manageVoPageResult.setTotalCount(pushPage.getTotal());
        manageVoPageResult.setPageSize(pushPage.getSize());
        manageVoPageResult.setData(pushManageVoList);
        manageVoPageResult.setCode(ErrorCode.SUCCESS.getCode());
        return manageVoPageResult;
    }

    @Override
    public PushManageVo detail(Long id) {
        Push push = pushDao.getById(id);
        if (Objects.isNull(push)) {
            return null;
        }
        PushManageVo pushManageVo = new PushManageVo();
        pushManageVo.setId(push.getId());
        pushManageVo.setPushTitle(push.getPushTitle());
        pushManageVo.setPushContent(push.getPushContent());
        pushManageVo.setPushIcon(push.getPushIcon());
        pushManageVo.setPushType(push.getPushType());
        pushManageVo.setEnableSchedulePush(push.getEnableSchedulePush());
        pushManageVo.setSchedulePushTime(push.getSchedulePushTime());
        pushManageVo.setStatus(push.getStatus());
        List<PushRelationTag> pushRelationTagList = pushRelationTagDao.getListByPushId(push.getId());
        if (CollectionUtil.isNotEmpty(pushRelationTagList)) {
            List<Long> pushTagIdList = pushRelationTagList.stream().map(PushRelationTag::getPushTagId).distinct().collect(Collectors.toList());
            pushManageVo.setPushTagIdList(pushTagIdList);
        }
        return pushManageVo;
    }

    @Override
    public void updateStatus(Long id, Boolean status, SysMember sysMember) {
        Push push = pushDao.getById(id);
        if (Objects.isNull(push)) {
            throw new GlobalRunTimeException("推送不存在");
        }
        if (status && push.getStatus()) {
            return;
        }
        if (!status && !push.getStatus()) {
            return;
        }
        push.setStatus(status);
        Boolean enableSchedulePush = push.getEnableSchedulePush();
        if (status && enableSchedulePush && DateTime.now().isAfter(push.getSchedulePushTime())) {
            //上架且当前时间已经晚于自动推送时间，则不再推送
            push.setEnableSchedulePush(false);
            push.setSchedulePushTime(null);
        }
        pushDao.updateById(push);
        if (status && !push.getIsPushed() && !enableSchedulePush) {
            doPushMessage(push.getId(), sysMember);
        }
    }

    @Override
    public void doPushMessage(Long id, SysMember sysMember) {
        RLock lock = redissonClient.getLock(StrUtil.format("GETUI_PUSH_MESSAGE_{}", id));
        try {
            if (!lock.tryLock(1, TimeUnit.MINUTES)) {
                return;
            }
            Push push = pushDao.getById(id);
            if (Objects.isNull(push)) {
                throw new GlobalRunTimeException("推送不存在 id:" + id);
            }
            if (!push.getStatus()) {
                throw new GlobalRunTimeException("未上架，不能推送 id:" + id);
            }
            if (push.getIsPushed()) {
                throw new GlobalRunTimeException("已推送 id:" + id);
            }
            if (Objects.equals(push.getPushType().getCode(), PushManageTypeEnum.ALL_PUSH.getCode())) {
                //全量推送
                PushMessageRequest pushMessageRequest = new PushMessageRequest();
                pushMessageRequest.setPushBusinessType(PushBusinessTypeEnum.ELSE_PUSH);
                pushMessageRequest.setSource(PushSourceEnum.METAGO.getCode());
                pushMessageRequest.setPushType(PushTypeEnum.PUSH_BY_FAST_CUSTOM_TAG);
                pushMessageRequest.setTag(PushNoticeEnableEnum.ENABLE_NOTICE.getKey());
                PushMessageBodyRequest pushMessageBodyRequest = new PushMessageBodyRequest();
                pushMessageBodyRequest.setTitle(push.getPushTitle());
                pushMessageBodyRequest.setContent(push.getPushContent());
                pushMessageBodyRequest.setImgUrl(push.getPushIcon());
                pushMessageBodyRequest.setUrl("https://www.gometa.com.cn/officalH5");
                pushMessageRequest.setBody(pushMessageBodyRequest);
                PushMessageExtendDTO pushMessageExtendDTO = new PushMessageExtendDTO();
                pushMessageExtendDTO.setPushUserId(Objects.nonNull(sysMember) ? sysMember.getId() : null);
                pushMessageExtendDTO.setBusinessId(id);
                pushMessageExtendDTO.setBusinessObject(PushMessageExtendBusinessEnum.PUSH.getCode());
                pushMessageExtendDTO.setBusinessDesc("推送");
                pushMessageRequest.setExtend(JSON.toJSONString(pushMessageExtendDTO));
                CommonResult commonResult = metaPushPlatClient.pushMessage(pushMessageRequest);
                if (!Objects.equals(ResultCode.SUCCESS.getCode(), commonResult.getCode())) {
                    log.error(commonResult.getMessage());
                }
            } else if (Objects.equals(push.getPushType().getCode(), PushManageTypeEnum.TAG_PUSH.getCode())) {
                //标签推送
                List<PushRelationTag> pushRelationTagList = pushRelationTagDao.getListByPushId(push.getId());
                if (CollectionUtils.isNotEmpty(pushRelationTagList)) {
                    List<Long> pushTagIdList = pushRelationTagList.stream().map(PushRelationTag::getPushTagId).distinct().collect(Collectors.toList());
                    List<PushTag> pushTagList = pushTagDao.listByIds(pushTagIdList);
                    if (CollectionUtil.isNotEmpty(pushTagList)) {
                        //关联藏品列表
                        List<Long> collectionIdList = pushTagList.stream().filter(pushTag -> Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_COLLECTION.getCode()))
                                .map(PushTag::getCollectionId).distinct().collect(Collectors.toList());
                        List<Long> relationTagIdList = pushTagList.stream().filter(pushTag -> Objects.equals(pushTag.getLinkType().getCode(), PushTagLinkTypeEnum.RELATION_USER.getCode()))
                                .map(PushTag::getId).collect(Collectors.toList());
                        List<Long> userIdList = new ArrayList<>();
                        if (CollectionUtil.isNotEmpty(collectionIdList)) {
                            //藏品关联用户
                            List<UserCollection> userCollectionList = userCollectionDao.getListByCollectionIds(collectionIdList);
                            if (CollectionUtil.isNotEmpty(userCollectionList)) {
                                userIdList.addAll(userCollectionList.stream().map(UserCollection::getUserId).collect(Collectors.toList()));
                            }
                        }
                        if (CollectionUtil.isNotEmpty(relationTagIdList)) {
                            //标签关联用户
                            List<PushTagRelationUser> pushTagRelationUserList = pushTagRelationUserDao.getListByPushTagIdList(relationTagIdList);
                            if (CollectionUtil.isNotEmpty(pushTagRelationUserList)) {
                                userIdList.addAll(pushTagRelationUserList.stream().map(PushTagRelationUser::getUserId).collect(Collectors.toList()));
                            }
                        }
                        if (CollectionUtil.isNotEmpty(userIdList)) {
                            userIdList = userIdList.stream().distinct().collect(Collectors.toList());
                            List<UserRelationPushCid> userRelationPushCidList = new ArrayList<>();
                            Long pageNo = 1L;
                            Long pageSize = 1000L;
                            Page<UserRelationPushCid> userRelationPushCidPage = new Page<>();
                            do {
                                userRelationPushCidPage = userRelationPushCidDao.getPageByUserIdList(new Page<>(pageNo, pageSize), userIdList);
                                List<UserRelationPushCid> records = userRelationPushCidPage.getRecords();
                                if (CollectionUtil.isNotEmpty(records)) {
                                    userRelationPushCidList.addAll(records);
                                }
                                pageNo++;
                            } while (pageNo <= userRelationPushCidPage.getPages());
                            if (CollectionUtil.isNotEmpty(userRelationPushCidList)) {
                                List<String> cid = userRelationPushCidList.stream().map(UserRelationPushCid::getCid).distinct().collect(Collectors.toList());
                                PushMessageRequest pushMessageRequest = new PushMessageRequest();
                                pushMessageRequest.setPushBusinessType(PushBusinessTypeEnum.ELSE_PUSH);
                                pushMessageRequest.setSource(PushSourceEnum.METAGO.getCode());
                                pushMessageRequest.setPushType(PushTypeEnum.BATCH_PUSH_BY_CID);
                                pushMessageRequest.setCid(cid);
                                PushMessageBodyRequest pushMessageBodyRequest = new PushMessageBodyRequest();
                                pushMessageBodyRequest.setTitle(push.getPushTitle());
                                pushMessageBodyRequest.setContent(push.getPushContent());
                                pushMessageBodyRequest.setImgUrl(push.getPushIcon());
                                pushMessageBodyRequest.setUrl("https://www.gometa.com.cn/officalH5");
                                pushMessageRequest.setBody(pushMessageBodyRequest);
                                PushMessageExtendDTO pushMessageExtendDTO = new PushMessageExtendDTO();
                                pushMessageExtendDTO.setPushUserId(Objects.nonNull(sysMember) ? sysMember.getId() : null);
                                pushMessageExtendDTO.setBusinessId(id);
                                pushMessageExtendDTO.setBusinessObject(PushMessageExtendBusinessEnum.PUSH.getCode());
                                pushMessageExtendDTO.setBusinessDesc("推送");
                                pushMessageRequest.setExtend(JSON.toJSONString(pushMessageExtendDTO));
                                CommonResult commonResult = metaPushPlatClient.pushMessage(pushMessageRequest);
                                if (!Objects.equals(ResultCode.SUCCESS.getCode(), commonResult.getCode())) {
                                    log.error(commonResult.getMessage());
                                }
                            }

                        }
                    }
                }
            }
            push.setIsPushed(true);
            push.setPushedTime(DateTime.now());
            pushDao.updateById(push);
        } catch (Exception e) {
            log.error("推送消息异常", e);
        } finally {
            lock.unlock();
        }
    }

    @Override
    public void doPushNoticeMessage(Long id) {
        RLock lock = redissonClient.getLock(StrUtil.format("GETUI_PUSH_NOTICE_MESSAGE_{}", id));
        try {
            if (!lock.tryLock(1, TimeUnit.MINUTES)) {
                return;
            }
            NoticeInfo noticeInfo = noticeInfoDao.getById(id);
            if (Objects.isNull(noticeInfo)) {
                return;
            }
            if (noticeInfo.getIsPushed()) {
                return;
            }
            noticeInfo.setIsPushed(true);
            noticeInfo.setPushedTime(DateTime.now());
            if (noticeInfo.updateById()) {
                PushMessageRequest pushMessageRequest = new PushMessageRequest();
                pushMessageRequest.setPushBusinessType(PushBusinessTypeEnum.ELSE_PUSH);
                pushMessageRequest.setSource(PushSourceEnum.METAGO.getCode());
                pushMessageRequest.setPushType(PushTypeEnum.PUSH_BY_FAST_CUSTOM_TAG);
                pushMessageRequest.setTag(PushNoticeEnableEnum.ENABLE_NOTICE.getKey());
                PushMessageBodyRequest pushMessageBodyRequest = new PushMessageBodyRequest();
                pushMessageBodyRequest.setTitle("您订阅的官方公告通知");
                pushMessageBodyRequest.setContent(noticeInfo.getNoticeTitle());
                pushMessageBodyRequest.setUrl("https://www.gometa.com.cn/officalH5");
                pushMessageRequest.setBody(pushMessageBodyRequest);
                PushMessageExtendDTO pushMessageExtendDTO = new PushMessageExtendDTO();
                pushMessageExtendDTO.setBusinessId(noticeInfo.getId());
                pushMessageExtendDTO.setBusinessObject(PushMessageExtendBusinessEnum.NOTICE_INFO.getCode());
                pushMessageExtendDTO.setBusinessDesc("新增公告");
                pushMessageRequest.setExtend(JSON.toJSONString(pushMessageExtendDTO));
                CommonResult commonResult = metaPushPlatClient.pushMessage(pushMessageRequest);
                if (!Objects.equals(ResultCode.SUCCESS.getCode(), commonResult.getCode())) {
                    log.error(commonResult.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("推送公告消息异常", e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 校验推送新增参数
     */
    private void checkAddPush(String pushTitle, PushManageTypeEnum pushType, Boolean enableSchedulePush, Date schedulePushTime, List<Long> pushTagIdList) {
        checkCommonPush(pushType, enableSchedulePush, schedulePushTime, pushTagIdList);
        List<Push> pushList = pushDao.getByTitle(pushTitle);
        if (CollectionUtil.isNotEmpty(pushList)) {
            throw new GlobalRunTimeException("推送标题已存在");
        }
    }

    /**
     * 校验推送编辑参数
     */
    private Push checkEditPush(Long id, String pushTitle, PushManageTypeEnum pushType, Boolean enableSchedulePush, Date schedulePushTime, List<Long> pushTagIdList) {
        checkCommonPush(pushType, enableSchedulePush, schedulePushTime, pushTagIdList);
        if (Objects.isNull(id)) {
            throw new GlobalRunTimeException("ID不能为空");
        }
        List<Push> pushList = pushDao.getByTitle(pushTitle);
        if (CollectionUtil.isNotEmpty(pushList)) {
            for (Push push : pushList) {
                if (!Objects.equals(push.getId(), id)) {
                    throw new GlobalRunTimeException("推送标题已存在");
                }
            }
        }
        Push push = pushDao.getById(id);
        if (Objects.isNull(push)) {
            throw new GlobalRunTimeException("推送不存在");
        }
        if (push.getStatus()) {
            throw new GlobalRunTimeException("上架中，不能编辑");
        }
        return push;
    }

    private void checkCommonPush(PushManageTypeEnum pushType, Boolean enableSchedulePush, Date schedulePushTime, List<Long> pushTagIdList) {
        if (pushType.getCode().equals(PushManageTypeEnum.TAG_PUSH.getCode())) {
            //如果是按标签推送，标签不能为空
            if (CollectionUtil.isEmpty(pushTagIdList)) {
                throw new GlobalRunTimeException("标签不能为空");
            }
        }
        if (enableSchedulePush) {
            //如果开启定时推送，推送时间不能为空，且不能早于当前时间
            if (Objects.isNull(schedulePushTime)) {
                throw new GlobalRunTimeException("推送时间不能为空");
            }
            if (DateTime.now().isAfter(schedulePushTime)) {
                throw new GlobalRunTimeException("推送时间早于当前时间");
            }
        }
    }

}
